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
.utils
.StringUtils
;
18 * Support class for <a href="http://www.fanfiction.net/">Faniction.net</a>
19 * stories, a website dedicated to fanfictions of many, many different
20 * universes, from TV shows to novels to games.
24 class Fanfiction
extends BasicSupport
{
26 protected boolean isHtml() {
31 public String
getSourceName() {
32 return "Fanfiction.net";
36 protected String
getSubject(URL source
, InputStream in
) {
37 String line
= getLine(in
, "id=pre_story_links", 0);
39 int pos
= line
.lastIndexOf('"');
41 line
= line
.substring(pos
+ 1);
42 pos
= line
.indexOf('<');
44 return line
.substring(0, pos
);
53 protected List
<String
> getTags(URL source
, InputStream in
)
55 List
<String
> tags
= super.getTags(source
, in
);
57 String key
= "title=\"Send Private Message\"";
58 String line
= getLine(in
, key
, 2);
61 int pos
= line
.indexOf(key
);
63 line
= line
.substring(pos
+ key
.length());
65 pos
= line
.indexOf(key
);
67 line
= line
.substring(0, pos
);
68 line
= StringUtils
.unhtml(line
).trim();
69 if (line
.endsWith("-")) {
70 line
= line
.substring(0, line
.length() - 1);
73 for (String tag
: line
.split("-")) {
84 protected String
getTitle(URL source
, InputStream in
) {
86 @SuppressWarnings("resource")
87 Scanner scan
= new Scanner(in
, "UTF-8");
88 scan
.useDelimiter("\\n");
89 while (scan
.hasNext()) {
90 String line
= scan
.next();
91 if (line
.contains("xcontrast_txt")) {
93 line
= StringUtils
.unhtml(line
).trim();
94 if (line
.startsWith("Follow/Fav")) {
95 line
= line
.substring("Follow/Fav".length()).trim();
107 protected String
getAuthor(URL source
, InputStream in
) {
109 @SuppressWarnings("resource")
110 Scanner scan
= new Scanner(in
, "UTF-8");
111 scan
.useDelimiter("\\n");
112 while (scan
.hasNext()) {
113 String line
= scan
.next();
114 if (line
.contains("xcontrast_txt")) {
116 return StringUtils
.unhtml(line
).trim();
125 protected String
getDate(URL source
, InputStream in
) {
126 String key
= "Published: <span data-xutime='";
127 String line
= getLine(in
, key
, 0);
129 int pos
= line
.indexOf(key
);
131 line
= line
.substring(pos
+ key
.length());
132 pos
= line
.indexOf('\'');
134 line
= line
.substring(0, pos
).trim();
136 SimpleDateFormat sdf
= new SimpleDateFormat(
139 .format(new Date(1000 * Long
.parseLong(line
)));
140 } catch (NumberFormatException e
) {
141 Instance
.syserr(new IOException(
142 "Cannot convert publication date: " + line
, e
));
152 protected String
getDesc(URL source
, InputStream in
) {
153 return getLine(in
, "title=\"Send Private Message\"", 1);
157 protected URL
getCover(URL url
, InputStream in
) {
158 String key
= "class='cimage";
159 String line
= getLine(in
, key
, 0);
161 int pos
= line
.indexOf(key
);
163 line
= line
.substring(pos
+ key
.length());
165 pos
= line
.indexOf(key
);
167 line
= line
.substring(pos
+ key
.length());
168 pos
= line
.indexOf('\'');
170 line
= line
.substring(0, pos
);
171 if (line
.startsWith("//")) {
172 line
= url
.getProtocol() + "://"
174 } else if (line
.startsWith("//")) {
175 line
= url
.getProtocol() + "://" + url
.getHost()
176 + "/" + line
.substring(1);
178 line
= url
.getProtocol() + "://" + url
.getHost()
179 + "/" + url
.getPath() + "/" + line
;
183 return new URL(line
);
184 } catch (MalformedURLException e
) {
196 protected List
<Entry
<String
, URL
>> getChapters(URL source
, InputStream in
) {
197 List
<Entry
<String
, URL
>> urls
= new ArrayList
<Entry
<String
, URL
>>();
199 String base
= source
.toString();
200 int pos
= base
.lastIndexOf('/');
201 String suffix
= base
.substring(pos
); // including '/' at start
202 base
= base
.substring(0, pos
);
203 if (base
.endsWith("/1")) {
204 base
= base
.substring(0, base
.length() - 1); // including '/' at end
207 String line
= getLine(in
, "id=chap_select", 0);
208 String key
= "<option value=";
210 for (pos
= line
.indexOf(key
); pos
>= 0; pos
= line
.indexOf(key
, pos
), i
++) {
211 pos
= line
.indexOf('>', pos
);
213 int endOfName
= line
.indexOf('<', pos
);
214 if (endOfName
>= 0) {
215 String name
= line
.substring(pos
+ 1, endOfName
);
216 String chapNum
= i
+ ".";
217 if (name
.startsWith(chapNum
)) {
218 name
= name
.substring(chapNum
.length(), name
.length());
222 final String chapName
= name
.trim();
223 final URL chapURL
= new URL(base
+ i
+ suffix
);
224 urls
.add(new Entry
<String
, URL
>() {
225 public URL
setValue(URL value
) {
229 public URL
getValue() {
233 public String
getKey() {
237 } catch (MalformedURLException e
) {
238 Instance
.syserr(new IOException("Cannot parse chapter "
239 + i
+ " url: " + (base
+ i
+ suffix
), e
));
249 protected String
getChapterContent(URL source
, InputStream in
, int number
) {
250 StringBuilder builder
= new StringBuilder();
251 String startAt
= "class='storytext ";
252 String endAt1
= "function review_init";
253 String endAt2
= "id=chap_select";
256 @SuppressWarnings("resource")
257 Scanner scan
= new Scanner(in
, "UTF-8");
258 scan
.useDelimiter("\\n");
259 while (scan
.hasNext()) {
260 String line
= scan
.next();
261 if (!ok
&& line
.contains(startAt
)) {
263 } else if (ok
&& (line
.contains(endAt1
) || line
.contains(endAt2
))) {
269 // First line may contain the title and chap name again
270 if (builder
.length() == 0) {
271 int pos
= line
.indexOf("<hr");
273 line
= line
.substring(pos
);
277 builder
.append(line
);
281 return builder
.toString();
285 protected boolean supports(URL url
) {
286 return "fanfiction.net".equals(url
.getHost())
287 || "www.fanfiction.net".equals(url
.getHost());