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
.ArrayList
;
10 import java
.util
.List
;
11 import java
.util
.Map
.Entry
;
12 import java
.util
.Scanner
;
14 import be
.nikiroo
.fanfix
.Instance
;
15 import be
.nikiroo
.fanfix
.bundles
.Config
;
16 import be
.nikiroo
.fanfix
.data
.MetaData
;
17 import be
.nikiroo
.utils
.Image
;
18 import be
.nikiroo
.utils
.Progress
;
19 import be
.nikiroo
.utils
.StringUtils
;
22 * Support class for <a href="http://www.fanfiction.net/">Faniction.net</a>
23 * stories, a website dedicated to fanfictions of many, many different
24 * universes, from TV shows to novels to games.
28 class Fanfiction
extends BasicSupport_Deprecated
{
30 protected boolean isHtml() {
35 public String
getSourceName() {
36 return "Fanfiction.net";
40 protected MetaData
getMeta(URL source
, InputStream in
) throws IOException
{
41 MetaData meta
= new MetaData();
43 meta
.setTitle(getTitle(reset(in
)));
44 meta
.setAuthor(getAuthor(reset(in
)));
45 meta
.setDate(getDate(reset(in
)));
46 meta
.setTags(getTags(reset(in
)));
47 meta
.setSource(getSourceName());
48 meta
.setUrl(source
.toString());
49 meta
.setPublisher(getSourceName());
50 meta
.setUuid(source
.toString());
52 meta
.setLang("en"); //TODO!
53 meta
.setSubject(getSubject(reset(in
)));
54 meta
.setType(getType().toString());
55 meta
.setImageDocument(false);
56 meta
.setCover(getCover(source
, reset(in
)));
61 private String
getSubject(InputStream in
) {
62 String line
= getLine(in
, "id=pre_story_links", 0);
64 int pos
= line
.lastIndexOf('"');
66 line
= line
.substring(pos
+ 1);
67 pos
= line
.indexOf('<');
69 return StringUtils
.unhtml(line
.substring(0, pos
)).trim();
77 private List
<String
> getTags(InputStream in
) {
78 List
<String
> tags
= new ArrayList
<String
>();
80 String key
= "title=\"Send Private Message\"";
81 String line
= getLine(in
, key
, 2);
84 int pos
= line
.indexOf(key
);
86 line
= line
.substring(pos
+ key
.length());
88 pos
= line
.indexOf(key
);
90 line
= line
.substring(0, pos
);
91 line
= StringUtils
.unhtml(line
).trim();
92 if (line
.endsWith("-")) {
93 line
= line
.substring(0, line
.length() - 1);
96 for (String tag
: line
.split("-")) {
97 tags
.add(StringUtils
.unhtml(tag
).trim());
106 private String
getTitle(InputStream in
) {
108 @SuppressWarnings("resource")
109 Scanner scan
= new Scanner(in
, "UTF-8");
110 scan
.useDelimiter("\\n");
111 while (scan
.hasNext()) {
112 String line
= scan
.next();
113 if (line
.contains("xcontrast_txt")) {
115 line
= StringUtils
.unhtml(line
).trim();
116 if (line
.startsWith("Follow/Fav")) {
117 line
= line
.substring("Follow/Fav".length()).trim();
120 return StringUtils
.unhtml(line
).trim();
128 private String
getAuthor(InputStream in
) {
129 String author
= null;
132 @SuppressWarnings("resource")
133 Scanner scan
= new Scanner(in
, "UTF-8");
134 scan
.useDelimiter("\\n");
135 while (scan
.hasNext()) {
136 String line
= scan
.next();
137 if (line
.contains("xcontrast_txt")) {
139 author
= StringUtils
.unhtml(line
).trim();
145 return BasicSupportHelper
.fixAuthor(author
);
148 private String
getDate(InputStream in
) {
149 String key
= "Published: <span data-xutime='";
150 String line
= getLine(in
, key
, 0);
152 int pos
= line
.indexOf(key
);
154 line
= line
.substring(pos
+ key
.length());
155 pos
= line
.indexOf('\'');
157 line
= line
.substring(0, pos
).trim();
159 SimpleDateFormat sdf
= new SimpleDateFormat(
162 .format(new Date(1000 * Long
.parseLong(line
)));
163 } catch (NumberFormatException e
) {
164 Instance
.getTraceHandler().error(
166 "Cannot convert publication date: "
177 protected String
getDesc(URL source
, InputStream in
) {
178 return getLine(in
, "title=\"Send Private Message\"", 1);
181 private Image
getCover(URL url
, InputStream in
) {
182 String key
= "class='cimage";
183 String line
= getLine(in
, key
, 0);
185 int pos
= line
.indexOf(key
);
187 line
= line
.substring(pos
+ key
.length());
189 pos
= line
.indexOf(key
);
191 line
= line
.substring(pos
+ key
.length());
192 pos
= line
.indexOf('\'');
194 line
= line
.substring(0, pos
);
195 if (line
.startsWith("//")) {
196 line
= url
.getProtocol() + "://"
198 } else if (line
.startsWith("//")) {
199 line
= url
.getProtocol() + "://" + url
.getHost()
200 + "/" + line
.substring(1);
202 line
= url
.getProtocol() + "://" + url
.getHost()
203 + "/" + url
.getPath() + "/" + line
;
206 return getImage(this, null, line
);
216 protected List
<Entry
<String
, URL
>> getChapters(URL source
, InputStream in
,
218 List
<Entry
<String
, URL
>> urls
= new ArrayList
<Entry
<String
, URL
>>();
220 String base
= source
.toString();
221 int pos
= base
.lastIndexOf('/');
222 String suffix
= base
.substring(pos
); // including '/' at start
223 base
= base
.substring(0, pos
);
224 if (base
.endsWith("/1")) {
225 base
= base
.substring(0, base
.length() - 1); // including '/' at end
228 String line
= getLine(in
, "id=chap_select", 0);
229 String key
= "<option value=";
233 for (pos
= line
.indexOf(key
); pos
>= 0; pos
= line
234 .indexOf(key
, pos
), i
++) {
235 pos
= line
.indexOf('>', pos
);
237 int endOfName
= line
.indexOf('<', pos
);
238 if (endOfName
>= 0) {
239 String name
= line
.substring(pos
+ 1, endOfName
);
240 String chapNum
= i
+ ".";
241 if (name
.startsWith(chapNum
)) {
242 name
= name
.substring(chapNum
.length(),
247 final String chapName
= name
.trim();
248 final URL chapURL
= new URL(base
+ i
+ suffix
);
249 urls
.add(new Entry
<String
, URL
>() {
251 public URL
setValue(URL value
) {
256 public URL
getValue() {
261 public String
getKey() {
265 } catch (MalformedURLException e
) {
266 Instance
.getTraceHandler()
267 .error(new IOException(
268 "Cannot parse chapter " + i
270 + (base
+ i
+ suffix
), e
));
277 final String chapName
= getTitle(reset(in
));
278 final URL chapURL
= source
;
279 urls
.add(new Entry
<String
, URL
>() {
281 public URL
setValue(URL value
) {
286 public URL
getValue() {
291 public String
getKey() {
301 protected String
getChapterContent(URL source
, InputStream in
, int number
,
303 StringBuilder builder
= new StringBuilder();
304 String startAt
= "class='storytext ";
305 String endAt1
= "function review_init";
306 String endAt2
= "id=chap_select";
309 @SuppressWarnings("resource")
310 Scanner scan
= new Scanner(in
, "UTF-8");
311 scan
.useDelimiter("\\n");
312 while (scan
.hasNext()) {
313 String line
= scan
.next();
314 if (!ok
&& line
.contains(startAt
)) {
316 } else if (ok
&& (line
.contains(endAt1
) || line
.contains(endAt2
))) {
322 // First line may contain the title and chap name again
323 if (builder
.length() == 0) {
324 int pos
= line
.indexOf("<hr");
326 boolean chaptered
= false;
327 for (String lang
: Instance
.getConfig()
328 .getString(Config
.CHAPTER
).split(",")) {
329 String chapterWord
= Instance
.getConfig()
330 .getStringX(Config
.CHAPTER
, lang
);
331 int posChap
= line
.indexOf(chapterWord
+ " ");
339 line
= line
.substring(pos
);
344 builder
.append(line
);
349 return builder
.toString();
353 protected boolean supports(URL url
) {
354 return "fanfiction.net".equals(url
.getHost())
355 || "www.fanfiction.net".equals(url
.getHost());