31bf72577bddbaae4d5af8f3ca4ab5fec6087d12
[fanfix.git] / src / be / nikiroo / fanfix / supported / Epub.java
1 package be.nikiroo.fanfix.supported;
2
3 import java.awt.image.BufferedImage;
4 import java.io.File;
5 import java.io.FileInputStream;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.net.URL;
9 import java.util.List;
10 import java.util.Map.Entry;
11 import java.util.zip.ZipEntry;
12 import java.util.zip.ZipInputStream;
13
14 import javax.imageio.ImageIO;
15
16 import be.nikiroo.fanfix.Instance;
17 import be.nikiroo.fanfix.bundles.Config;
18 import be.nikiroo.utils.IOUtils;
19 import be.nikiroo.utils.MarkableFileInputStream;
20
21 /**
22 * Support class for EPUB files created with this program (as we need some
23 * metadata available in those we create).
24 *
25 * @author niki
26 */
27 class Epub extends BasicSupport {
28 private InfoText base;
29 private URL fakeSource;
30
31 private File tmpCover;
32 private File tmpInfo;
33 private File tmp;
34
35 /** Only used by {@link Epub#getInput()} so it is always reset. */
36 private InputStream in;
37
38 @Override
39 public String getSourceName() {
40 return "epub";
41 }
42
43 @Override
44 protected boolean supports(URL url) {
45 if (url.getPath().toLowerCase().endsWith(".epub")) {
46 return true;
47 }
48
49 return false;
50 }
51
52 @Override
53 protected boolean isHtml() {
54 if (tmpInfo.exists()) {
55 return base.isHtml();
56 }
57
58 return false;
59 }
60
61 @Override
62 protected String getTitle(URL source, InputStream in) throws IOException {
63 if (tmpInfo.exists()) {
64 return base.getTitle(fakeSource, getFakeInput());
65 }
66
67 return source.toString();
68 }
69
70 @Override
71 protected String getAuthor(URL source, InputStream in) throws IOException {
72 if (tmpInfo.exists()) {
73 return base.getAuthor(fakeSource, getFakeInput());
74 }
75
76 return null;
77 }
78
79 @Override
80 protected String getDate(URL source, InputStream in) throws IOException {
81 if (tmpInfo.exists()) {
82 return base.getDate(fakeSource, getFakeInput());
83 }
84
85 return null;
86 }
87
88 @Override
89 protected String getSubject(URL source, InputStream in) throws IOException {
90 if (tmpInfo.exists()) {
91 return base.getSubject(fakeSource, getFakeInput());
92 }
93
94 return null;
95 }
96
97 @Override
98 protected String getDesc(URL source, InputStream in) throws IOException {
99 if (tmpInfo.exists()) {
100 return base.getDesc(fakeSource, getFakeInput());
101 }
102
103 return null;
104 }
105
106 @Override
107 protected URL getCover(URL source, InputStream in) throws IOException {
108 if (tmpCover.exists()) {
109 return tmpCover.toURI().toURL();
110 }
111
112 return null;
113 }
114
115 @Override
116 protected List<Entry<String, URL>> getChapters(URL source, InputStream in)
117 throws IOException {
118 if (tmpInfo.exists()) {
119 return base.getChapters(fakeSource, getFakeInput());
120 }
121
122 return null;
123 }
124
125 @Override
126 protected String getChapterContent(URL source, InputStream in, int number)
127 throws IOException {
128 if (tmpInfo.exists()) {
129 return base.getChapterContent(fakeSource, getFakeInput(), number);
130 }
131
132 return null;
133 }
134
135 @Override
136 protected String getLang(URL source, InputStream in) throws IOException {
137 if (tmpInfo.exists()) {
138 return base.getLang(fakeSource, getFakeInput());
139 }
140
141 return super.getLang(source, in);
142 }
143
144 @Override
145 protected String getPublisher(URL source, InputStream in)
146 throws IOException {
147 if (tmpInfo.exists()) {
148 return base.getPublisher(fakeSource, getFakeInput());
149 }
150
151 return super.getPublisher(source, in);
152 }
153
154 @Override
155 protected List<String> getTags(URL source, InputStream in)
156 throws IOException {
157 if (tmpInfo.exists()) {
158 return base.getTags(fakeSource, getFakeInput());
159 }
160
161 return super.getTags(source, in);
162 }
163
164 @Override
165 protected String getUuid(URL source, InputStream in) throws IOException {
166 if (tmpInfo.exists()) {
167 return base.getUuid(fakeSource, getFakeInput());
168 }
169
170 return super.getUuid(source, in);
171 }
172
173 @Override
174 protected String getLuid(URL source, InputStream in) throws IOException {
175 if (tmpInfo.exists()) {
176 return base.getLuid(fakeSource, getFakeInput());
177 }
178
179 return super.getLuid(source, in);
180 }
181
182 @Override
183 protected void preprocess(InputStream in) throws IOException {
184 // Note: do NOT close this stream, as it would also close "in"
185 ZipInputStream zipIn = new ZipInputStream(in);
186 tmp = File.createTempFile("fanfic-reader-parser_", ".tmp");
187 tmpInfo = new File(tmp + ".info");
188 tmpCover = File.createTempFile("fanfic-reader-parser_", ".tmp");
189
190 base = new InfoText();
191 fakeSource = tmp.toURI().toURL();
192
193 for (ZipEntry entry = zipIn.getNextEntry(); entry != null; entry = zipIn
194 .getNextEntry()) {
195 if (!entry.isDirectory()
196 && entry.getName().startsWith(getDataPrefix())) {
197 String entryLName = entry.getName().toLowerCase();
198 boolean imageEntry = false;
199 for (String ext : getImageExt(false)) {
200 if (entryLName.endsWith(ext)) {
201 imageEntry = true;
202 }
203 }
204
205 if (entry.getName().equals(getDataPrefix() + "version")) {
206 // Nothing to do for now ("first"
207 // version is 3.0)
208 } else if (entryLName.endsWith(".info")) {
209 // Info file
210 IOUtils.write(zipIn, tmpInfo);
211 } else if (imageEntry) {
212 // Cover
213 if (getCover()) {
214 try {
215 BufferedImage image = ImageIO.read(zipIn);
216 ImageIO.write(image, Instance.getConfig()
217 .getString(Config.IMAGE_FORMAT_COVER)
218 .toLowerCase(), tmpCover);
219 } catch (Exception e) {
220 Instance.syserr(e);
221 }
222 }
223 } else if (entry.getName().equals(getDataPrefix() + "URL")) {
224 // Do nothing
225 } else if (entry.getName().equals(getDataPrefix() + "SUMMARY")) {
226 // Do nothing
227 } else {
228 // Hopefully the data file
229 IOUtils.write(zipIn, tmp);
230 }
231 }
232 }
233
234 if (requireInfo() && (!tmp.exists() || !tmpInfo.exists())) {
235 throw new IOException(
236 "file not supported (maybe not created with this program or corrupt)");
237 }
238
239 if (tmp.exists()) {
240 this.in = new MarkableFileInputStream(new FileInputStream(tmp));
241 }
242 }
243
244 @Override
245 protected void close() throws IOException {
246 for (File file : new File[] { tmp, tmpInfo, tmpCover }) {
247 if (file != null && file.exists()) {
248 if (!file.delete()) {
249 file.deleteOnExit();
250 }
251 }
252 }
253
254 tmp = null;
255 tmpInfo = null;
256 tmpCover = null;
257 fakeSource = null;
258
259 try {
260 if (in != null) {
261 in.close();
262 }
263 } finally {
264 in = null;
265 base.close();
266 }
267 }
268
269 protected String getDataPrefix() {
270 return "DATA/";
271 }
272
273 protected boolean requireInfo() {
274 return true;
275 }
276
277 protected boolean getCover() {
278 return true;
279 }
280
281 /**
282 * Reset then return {@link Epub#in}.
283 *
284 * @return {@link Epub#in}
285 *
286 * @throws IOException
287 * in case of I/O error
288 */
289 private InputStream getFakeInput() throws IOException {
290 in.reset();
291 return in;
292 }
293 }