1 package be
.nikiroo
.fanfix
.supported
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
5 import java
.net
.MalformedURLException
;
7 import java
.text
.SimpleDateFormat
;
8 import java
.util
.AbstractMap
;
9 import java
.util
.ArrayList
;
10 import java
.util
.Date
;
11 import java
.util
.List
;
12 import java
.util
.Map
.Entry
;
13 import java
.util
.Scanner
;
15 import be
.nikiroo
.fanfix
.Instance
;
16 import be
.nikiroo
.fanfix
.bundles
.Config
;
17 import be
.nikiroo
.fanfix
.data
.MetaData
;
18 import be
.nikiroo
.utils
.Image
;
19 import be
.nikiroo
.utils
.Progress
;
20 import be
.nikiroo
.utils
.StringUtils
;
23 * Support class for <a href="http://www.fanfiction.net/">Faniction.net</a>
24 * stories, a website dedicated to fanfictions of many, many different
25 * universes, from TV shows to novels to games.
29 class Fanfiction
extends BasicSupport_Deprecated
{
31 protected boolean isHtml() {
36 protected MetaData
getMeta(URL source
, InputStream in
) throws IOException
{
37 MetaData meta
= new MetaData();
39 meta
.setTitle(getTitle(reset(in
)));
40 meta
.setAuthor(getAuthor(reset(in
)));
41 meta
.setDate(getDate(reset(in
)));
42 meta
.setTags(getTags(reset(in
)));
43 meta
.setUrl(source
.toString());
44 meta
.setUuid(source
.toString());
46 meta
.setLang("en"); // TODO find language of book
47 meta
.setSubject(getSubject(reset(in
)));
48 meta
.setImageDocument(false);
49 meta
.setCover(getCover(source
, reset(in
)));
54 private String
getSubject(InputStream in
) {
55 String line
= getLine(in
, "id=pre_story_links", 0);
57 int pos
= line
.lastIndexOf('"');
59 line
= line
.substring(pos
+ 1);
60 pos
= line
.indexOf('<');
62 return StringUtils
.unhtml(line
.substring(0, pos
)).trim();
70 private List
<String
> getTags(InputStream in
) {
71 List
<String
> tags
= new ArrayList
<String
>();
73 String key
= "title=\"Send Private Message\"";
74 String line
= getLine(in
, key
, 2);
77 int pos
= line
.indexOf(key
);
79 line
= line
.substring(pos
+ key
.length());
81 pos
= line
.indexOf(key
);
83 line
= line
.substring(0, pos
);
84 line
= StringUtils
.unhtml(line
).trim();
85 if (line
.endsWith("-")) {
86 line
= line
.substring(0, line
.length() - 1);
89 for (String tag
: line
.split("-")) {
90 tags
.add(StringUtils
.unhtml(tag
).trim());
99 private String
getTitle(InputStream in
) {
101 @SuppressWarnings("resource")
102 Scanner scan
= new Scanner(in
, "UTF-8");
103 scan
.useDelimiter("\\n");
104 while (scan
.hasNext()) {
105 String line
= scan
.next();
106 if (line
.contains("xcontrast_txt")) {
108 line
= StringUtils
.unhtml(line
).trim();
109 if (line
.startsWith("Follow/Fav")) {
110 line
= line
.substring("Follow/Fav".length()).trim();
113 return StringUtils
.unhtml(line
).trim();
121 private String
getAuthor(InputStream in
) {
122 String author
= null;
125 @SuppressWarnings("resource")
126 Scanner scan
= new Scanner(in
, "UTF-8");
127 scan
.useDelimiter("\\n");
128 while (scan
.hasNext()) {
129 String line
= scan
.next();
130 if (line
.contains("xcontrast_txt")) {
132 author
= StringUtils
.unhtml(line
).trim();
138 return bsHelper
.fixAuthor(author
);
141 private String
getDate(InputStream in
) {
142 String key
= "Published: <span data-xutime='";
143 String line
= getLine(in
, key
, 0);
145 int pos
= line
.indexOf(key
);
147 line
= line
.substring(pos
+ key
.length());
148 pos
= line
.indexOf('\'');
150 line
= line
.substring(0, pos
).trim();
152 SimpleDateFormat sdf
= new SimpleDateFormat(
155 .format(new Date(1000 * Long
.parseLong(line
)));
156 } catch (NumberFormatException e
) {
157 Instance
.getInstance().getTraceHandler()
158 .error(new IOException("Cannot convert publication date: " + line
, e
));
168 protected String
getDesc(URL source
, InputStream in
) {
169 return getLine(in
, "title=\"Send Private Message\"", 1);
172 private Image
getCover(URL url
, InputStream in
) {
173 String key
= "class='cimage";
174 String line
= getLine(in
, key
, 0);
176 int pos
= line
.indexOf(key
);
178 line
= line
.substring(pos
+ key
.length());
180 pos
= line
.indexOf(key
);
182 line
= line
.substring(pos
+ key
.length());
183 pos
= line
.indexOf('\'');
185 line
= line
.substring(0, pos
);
186 if (line
.startsWith("//")) {
187 line
= url
.getProtocol() + "://"
189 } else if (line
.startsWith("//")) {
190 line
= url
.getProtocol() + "://" + url
.getHost()
191 + "/" + line
.substring(1);
193 line
= url
.getProtocol() + "://" + url
.getHost()
194 + "/" + url
.getPath() + "/" + line
;
197 return getImage(this, null, line
);
207 protected List
<Entry
<String
, URL
>> getChapters(URL source
, InputStream in
,
209 List
<Entry
<String
, URL
>> urls
= new ArrayList
<Entry
<String
, URL
>>();
211 String base
= source
.toString();
212 int pos
= base
.lastIndexOf('/');
213 String suffix
= base
.substring(pos
); // including '/' at start
214 base
= base
.substring(0, pos
);
215 if (base
.endsWith("/1")) {
216 base
= base
.substring(0, base
.length() - 1); // including '/' at end
219 String line
= getLine(in
, "id=chap_select", 0);
220 String key
= "<option value=";
224 for (pos
= line
.indexOf(key
); pos
>= 0; pos
= line
225 .indexOf(key
, pos
), i
++) {
226 pos
= line
.indexOf('>', pos
);
228 int endOfName
= line
.indexOf('<', pos
);
229 if (endOfName
>= 0) {
230 String name
= line
.substring(pos
+ 1, endOfName
);
231 String chapNum
= i
+ ".";
232 if (name
.startsWith(chapNum
)) {
233 name
= name
.substring(chapNum
.length(),
238 urls
.add(new AbstractMap
.SimpleEntry
<String
, URL
>(
239 name
.trim(), new URL(base
+ i
+ suffix
)));
240 } catch (MalformedURLException e
) {
241 Instance
.getInstance().getTraceHandler().error(
242 new IOException("Cannot parse chapter " + i
+ " url: " + (base
+ i
+ suffix
), e
));
249 final String chapName
= getTitle(reset(in
));
250 final URL chapURL
= source
;
251 urls
.add(new Entry
<String
, URL
>() {
253 public URL
setValue(URL value
) {
258 public URL
getValue() {
263 public String
getKey() {
273 protected String
getChapterContent(URL source
, InputStream in
, int number
,
275 StringBuilder builder
= new StringBuilder();
276 String startAt
= "class='storytext ";
277 String endAt1
= "function review_init";
278 String endAt2
= "id=chap_select";
281 @SuppressWarnings("resource")
282 Scanner scan
= new Scanner(in
, "UTF-8");
283 scan
.useDelimiter("\\n");
284 while (scan
.hasNext()) {
285 String line
= scan
.next();
286 if (!ok
&& line
.contains(startAt
)) {
288 } else if (ok
&& (line
.contains(endAt1
) || line
.contains(endAt2
))) {
294 // First line may contain the title and chap name again
295 if (builder
.length() == 0) {
296 int pos
= line
.indexOf("<hr");
298 boolean chaptered
= false;
299 for (String lang
: Instance
.getInstance().getConfig().getList(Config
.CONF_CHAPTER
)) {
300 String chapterWord
= Instance
.getInstance().getConfig().getStringX(Config
.CONF_CHAPTER
,
302 int posChap
= line
.indexOf(chapterWord
+ " ");
310 line
= line
.substring(pos
);
315 builder
.append(line
);
320 return builder
.toString();
324 protected boolean supports(URL url
) {
325 return "fanfiction.net".equals(url
.getHost())
326 || "www.fanfiction.net".equals(url
.getHost());