Initial commit (working)
[fanfix.git] / src / be / nikiroo / fanfix / supported / Fanfiction.java
1 package be.nikiroo.fanfix.supported;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.MalformedURLException;
6 import java.net.URL;
7 import java.text.SimpleDateFormat;
8 import java.util.ArrayList;
9 import java.util.Date;
10 import java.util.List;
11 import java.util.Map.Entry;
12 import java.util.Scanner;
13
14 import be.nikiroo.fanfix.Instance;
15 import be.nikiroo.utils.StringUtils;
16
17 /**
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.
21 *
22 * @author niki
23 */
24 class Fanfiction extends BasicSupport {
25 @Override
26 protected boolean isHtml() {
27 return true;
28 }
29
30 @Override
31 public String getSourceName() {
32 return "Fanfiction.net";
33 }
34
35 @Override
36 protected String getSubject(URL source, InputStream in) {
37 String line = getLine(in, "id=pre_story_links", 0);
38 if (line != null) {
39 int pos = line.lastIndexOf('"');
40 if (pos >= 1) {
41 line = line.substring(pos + 1);
42 pos = line.indexOf('<');
43 if (pos >= 0) {
44 return line.substring(0, pos);
45 }
46 }
47 }
48
49 return null;
50 }
51
52 @Override
53 protected List<String> getTags(URL source, InputStream in)
54 throws IOException {
55 List<String> tags = super.getTags(source, in);
56
57 String key = "title=\"Send Private Message\"";
58 String line = getLine(in, key, 2);
59 if (line != null) {
60 key = "Rated:";
61 int pos = line.indexOf(key);
62 if (pos >= 0) {
63 line = line.substring(pos + key.length());
64 key = "Chapters:";
65 pos = line.indexOf(key);
66 if (pos >= 0) {
67 line = line.substring(0, pos);
68 line = StringUtils.unhtml(line).trim();
69 if (line.endsWith("-")) {
70 line = line.substring(0, line.length() - 1);
71 }
72
73 for (String tag : line.split("-")) {
74 tags.add(tag.trim());
75 }
76 }
77 }
78 }
79
80 return tags;
81 }
82
83 @Override
84 protected String getTitle(URL source, InputStream in) {
85 int i = 0;
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")) {
92 if ((++i) == 2) {
93 line = StringUtils.unhtml(line).trim();
94 if (line.startsWith("Follow/Fav")) {
95 line = line.substring("Follow/Fav".length()).trim();
96 }
97
98 return line;
99 }
100 }
101 }
102
103 return null;
104 }
105
106 @Override
107 protected String getAuthor(URL source, InputStream in) {
108 int i = 0;
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")) {
115 if ((++i) == 3) {
116 return StringUtils.unhtml(line).trim();
117 }
118 }
119 }
120
121 return null;
122 }
123
124 @Override
125 protected String getDate(URL source, InputStream in) {
126 String key = "Published: <span data-xutime='";
127 String line = getLine(in, key, 0);
128 if (line != null) {
129 int pos = line.indexOf(key);
130 if (pos >= 0) {
131 line = line.substring(pos + key.length());
132 pos = line.indexOf('\'');
133 if (pos >= 0) {
134 line = line.substring(0, pos).trim();
135 try {
136 SimpleDateFormat sdf = new SimpleDateFormat(
137 "YYYY-MM-dd");
138 return sdf
139 .format(new Date(1000 * Long.parseLong(line)));
140 } catch (NumberFormatException e) {
141 Instance.syserr(new IOException(
142 "Cannot convert publication date: " + line, e));
143 }
144 }
145 }
146 }
147
148 return null;
149 }
150
151 @Override
152 protected String getDesc(URL source, InputStream in) {
153 return getLine(in, "title=\"Send Private Message\"", 1);
154 }
155
156 @Override
157 protected URL getCover(URL url, InputStream in) {
158 String key = "class='cimage";
159 String line = getLine(in, key, 0);
160 if (line != null) {
161 int pos = line.indexOf(key);
162 if (pos >= 0) {
163 line = line.substring(pos + key.length());
164 key = "src='";
165 pos = line.indexOf(key);
166 if (pos >= 0) {
167 line = line.substring(pos + key.length());
168 pos = line.indexOf('\'');
169 if (pos >= 0) {
170 line = line.substring(0, pos);
171 if (line.startsWith("//")) {
172 line = url.getProtocol() + "://"
173 + line.substring(2);
174 } else if (line.startsWith("//")) {
175 line = url.getProtocol() + "://" + url.getHost()
176 + "/" + line.substring(1);
177 } else {
178 line = url.getProtocol() + "://" + url.getHost()
179 + "/" + url.getPath() + "/" + line;
180 }
181
182 try {
183 return new URL(line);
184 } catch (MalformedURLException e) {
185 Instance.syserr(e);
186 }
187 }
188 }
189 }
190 }
191
192 return null;
193 }
194
195 @Override
196 protected List<Entry<String, URL>> getChapters(URL source, InputStream in) {
197 List<Entry<String, URL>> urls = new ArrayList<Entry<String, URL>>();
198
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
205 }
206
207 String line = getLine(in, "id=chap_select", 0);
208 String key = "<option value=";
209 int i = 1;
210 for (pos = line.indexOf(key); pos >= 0; pos = line.indexOf(key, pos), i++) {
211 pos = line.indexOf('>', pos);
212 if (pos >= 0) {
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());
219 }
220
221 try {
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) {
226 return null;
227 }
228
229 public URL getValue() {
230 return chapURL;
231 }
232
233 public String getKey() {
234 return chapName;
235 }
236 });
237 } catch (MalformedURLException e) {
238 Instance.syserr(new IOException("Cannot parse chapter "
239 + i + " url: " + (base + i + suffix), e));
240 }
241 }
242 }
243 }
244
245 return urls;
246 }
247
248 @Override
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";
254 boolean ok = false;
255
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)) {
262 ok = true;
263 } else if (ok && (line.contains(endAt1) || line.contains(endAt2))) {
264 ok = false;
265 break;
266 }
267
268 if (ok) {
269 // First line may contain the title and chap name again
270 if (builder.length() == 0) {
271 int pos = line.indexOf("<hr");
272 if (pos >= 0) {
273 line = line.substring(pos);
274 }
275 }
276
277 builder.append(line);
278 }
279 }
280
281 return builder.toString();
282 }
283
284 @Override
285 protected boolean supports(URL url) {
286 return "fanfiction.net".equals(url.getHost())
287 || "www.fanfiction.net".equals(url.getHost());
288 }
289 }